輸入為一個正整數 k,以及一個字串,字串不為空且其中字元都是小寫英文字母。將字串中每個字元在字母表中移動 k 個位置,回傳得到的新字串。
字元移動時需在字母表中循環,例如 'z' 移動一個位置應回傳 'a'。
sample input:
string = 'xyz'
k = 2
sample output:
'zab'
基本的想法是,遍歷過字串,對每個字母進行移位的運算後,放進一個陣列中,最後再將陣列元素合併為新字串。
而移位運算的部分,可以使用 Unicode 進行。大部分的程式語言都有內建的函式可以轉換 Unicode 和字元,可以以此來進行運算。
以虛擬碼來表示的話,若將字母移位時,在代碼上的變化可以理解為:
// 假設 toCode() 將字母轉為代碼
newCode = toCode(letter) + k
接下來要將新代碼轉回字母,且若超過字母表範圍,要再從 a 開始。可以以 a 和 z 的代碼 (97, 122) 以及餘數運算子 (%) 表達。
// 假設 toLetter() 將代碼轉為字母
if (newCode <= 122)
return toLetter(newCode)
else
return toLetter(96 + newCode % 122)
最後要考慮的是如果 k 是一個較大的數字可能會出現的問題。例如說當 k = 54,其實結果應該要跟 k = 2 一樣,因為每移動 26 個位置就會回到原本的字母。
但是用 54 運算的話,就算最後 % 122 還是會超出字母表的範圍。所以在一開始再加上以下一行,來避免錯誤:
k = k % 26
n 為字串長度
time: O(n)
space: O(n)
function caesarCipherEncryptor(string, k) {
const newLetters = [];
const newK = k % 26;
for (const letter of string) {
newLetters.push(getNewLetter(letter, newK));
}
return newLetters.join('');
}
function getNewLetter(letter, key) {
const newCode = letter.charCodeAt() + key;
return newCode <= 122 ? String.fromCharCode(newCode) : String.fromCharCode(96 + newCode % 122);
}
第二種解算是第一種的延伸,如果 Unicode 做的事情就是將字母跟代碼做連結,那麼也可以嘗試自己創造代碼,例如把字母放入陣列,以索引值作為字母對應的數字。若 a 的代碼為 0,z 的代碼為 25,那麼前述的虛擬碼可以修改為:
// 假設 alphabet.indexOf() 回傳字母表中索引
k = k % 26
newCode = alphabet.indexOf(letter) + k
return toLetter(newCode % 26)
這裡可以特別注意的是,虛擬碼並不是比照 Unicode 的版本,在 newCode > 25
的情況下回傳 toLetter(-1 + newCode % 25)
,是因為如果 newCode 整除 25,就會得到索引值為 -1 的錯誤。例如以下的輸入:
string = "z"
key = 25
前面 Unicode 的版本之所以不會有這個問題,是因為用來運算出 newCode 的 k 一定小於 26,且區塊內 newCode 一定大於 122,所以 newCode % 122 一定不會等於 0。
這個解法的時間和空間複雜度一樣都是 O(n)。另外可以特別提到的是使用 alphabet.indexOf 這樣的運算時,若字母表如本題只有 26 個字母,就只需要常數時間。但字母表若比較大,例如長度為 m,則運算需要 O(m) 時間。
function caesarCipherEncryptor(string, k) {
const newLetters = [];
const newK = k % 26;
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
for (const letter of string) {
newLetters.push(getNewLetter(letter, newK, alphabet));
}
return newLetters.join('');
}
function getNewLetter(letter, key, alphabet) {
const newCode = alphabet.indexOf(letter) + key;
return alphabet[newCode % 26];
}